# -*- coding: utf-8 -*-

import os
import sys

if __name__ == "__main__":
    currentdir = os.path.dirname( os.path.abspath( sys.argv[0] ) )
    basedir = os.path.abspath( os.path.join( currentdir, ".." ) )
    pythondir = basedir
    sys.path.insert( 0, pythondir )
    pass

from logger import get_file_logger
from datetime import datetime, timedelta
from picklerpc.msghandler import RequestHandler
from picklerpc.sendmsg import sendmsg
from picklerpc import Server
from time import time, sleep
from service import default_request_timeout, default_niceness_level

__all__ = ['StatisticsHandler', 'StatisticsServer', 'StatisticsManager', 'StatisticsRequest']

#---------------------------------------------

class StatisticsHandler(RequestHandler):

    def _request_handle(self, data):
        result = False
        
        if not (type(data) is dict):
            return result

        if data["task"] == "ping":
            return self.server.ping()
        if data["task"] == "quit":
            return self.server.quit()
        if data["task"] == "reset":
            return self.server.reset()
        if data["task"] == "inc":
            data = data["data"]
            return self.server.inc(data["param"], data["value"])
        if data["task"] == "dec":
            data = data["data"]
            return self.server.dec(data["param"], data["value"])
        if data["task"] == "set":
            data = data["data"]
            return self.server.set(data["param"], data["value"])
        if data["task"] == "is_updated":
            return self.server.is_updated()
        if data["task"] == "get_update":
            return self.server.get_update()
        if data["task"] == "set_hashing":
            data = data["data"]
            return self.server.set_hashing(
                data["device"],
                data["name"],
                data["size"],
                data["readed"],
                data["start_time"]
            )
        if data["task"] == "inc_hashing":
            data = data["data"]
            return self.server.inc_hashing(
                data["device"],
                data["readed"]
            )
        if data["task"] == "set_imaging":
            data = data["data"]
            return self.server.set_imaging(
                data["name"],
                data["count"],
                data["processed"],
                data["start_time"]
            )
        if data["task"] == "inc_imaging":
            data = data["data"]
            return self.server.inc_imaging(
                data["processed"]
            )
        return result
    
    pass

#---------------------------------------------

class StatisticsServer(Server):

    stats = None
    _lock = None

    log = None

    def server_activate(self):
        from project.statistics import Statistics
        from threading import Lock

        self.log = get_file_logger("StatisticsServer")

        self._lock = Lock()
        self.stats = Statistics()
        return Server.server_activate(self)

    def ping(self):
        return "pong"

    def reset(self):
        self._lock.acquire()
        self.stats.reset()
        self._lock.release()
        return True

    def inc(self, param, value):
        self._lock.acquire()
        self.stats.inc(param, value)
        self._lock.release()
        return True

    def dec(self, param, value):
        self._lock.acquire()
        self.stats.dec(param, value)
        self._lock.release()
        return True

    def set(self, param, value):
        self._lock.acquire()
        self.stats.set(param, value)
        self._lock.release()
        return True

    def is_updated(self):
        self._lock.acquire()
        u = self.stats.is_updated()
        self._lock.release()
        return u

    def get_update(self):
        self._lock.acquire()
        u = self.stats.get_update()
        self._lock.release()
        return u

    def set_hashing(self, device, name, size, readed, start_time):
        self._lock.acquire()
        self.stats.set_hashing( device, name, size, readed, start_time )
        self._lock.release()
        return True

    def inc_hashing(self, device, readed):
        self._lock.acquire()
        self.stats.inc_hashing( device, readed )
        self._lock.release()
        return True

    def set_imaging(self, name, count, processed, start_time):
        self._lock.acquire()
        self.stats.set_imaging( name, count, processed, start_time )
        self._lock.release()
        return True

    def inc_imaging(self, processed ):
        self._lock.acquire()
        self.stats.inc_imaging( processed )
        self._lock.release()
        return True

    def quit(self):
        self.log.info("quit event")

        import signal

        self.log.debug("send SIGINT to self")
        os.kill( os.getpid(), signal.SIGINT )

        return True


    def terminate_self(self, signum, frame):
        self.log.debug("got SHUTDOWN signal")
        t_begin = time()
        self.server_close()
        t_end = time()
        self.log.info("SHUTDOWN in %s sec" % (timedelta(seconds=t_end - t_begin),))
        return

    pass

#---------------------------------------------


class StatisticsRequest(object):

    manager = None

    def __init__(self, manager):
        self.manager = manager
        pass

    def ping(self):
        return self.manager.sendmsg("ping", {}, 0.1)

    def reset(self):
        return self.manager.sendmsg("reset", {})

    def inc(self, param, value):
        return self.manager.sendmsg("inc", {
            "param" : param,
            "value" : value
        })

    def dec(self, param, value):
        return self.manager.sendmsg("dec", {
            "param" : param,
            "value" : value
        })

    def set(self, param, value):
        return self.manager.sendmsg("set", {
            "param" : param,
            "value" : value
        })

    def is_updated(self):
        return self.manager.sendmsg("is_updated", {})

    def get_update(self):
        return self.manager.sendmsg("get_update", {})

    def set_hashing(self, device, name, size, readed, start_time):
        return self.manager.sendmsg("set_hashing", {
            "device" : device,
            "name" : name,
            "size" : size,
            "readed" : readed,
            "start_time" : start_time
        })

    def inc_hashing(self, device, readed):
        return self.manager.sendmsg("inc_hashing", {
            "device" : device,
            "readed" : readed
        })

    def set_imaging(self, name, count, processed, start_time):
        return self.manager.sendmsg("set_imaging", {
            "name" : name,
            "count" : count,
            "processed" : processed,
            "start_time" : start_time
        })

    def inc_imaging(self, processed):
        return self.manager.sendmsg("inc_imaging", {
            "processed" : processed
        })

    def quit(self):
        return self.manager.sendmsg("quit", {}, 0.1)

    def get_address(self):
        return self.manager.get_address()

    pass


class StatisticsDirectRequest(object):

    address = None

    def __init__(self, service_address):
        self.address = service_address
        self.log = get_file_logger("StatisticsDirectRequest")
        pass

    def sendmsg(self, task, data, timeout=default_request_timeout):
        try:
            result = sendmsg( self.address, {"task":task, "data":data}, timeout=timeout )
        except Exception as e:
            result = None
            self.log.error("SOCKET: %s" % ( e,))
            self.log.debug("FAIL SEND MESSAGE: task=%s, data=%r" % ( task, data,))
        return result

    def ping(self):
        return self.sendmsg("ping", {}, 0.1)

    def reset(self):
        return self.sendmsg("reset", {})

    def inc(self, param, value):
        return self.sendmsg("inc", {
            "param" : param,
            "value" : value
        })

    def dec(self, param, value):
        return self.sendmsg("dec", {
            "param" : param,
            "value" : value
        })

    def set(self, param, value):
        return self.sendmsg("set", {
            "param" : param,
            "value" : value
        })

    def is_updated(self):
        return self.sendmsg("is_updated", {})

    def get_update(self):
        return self.sendmsg("get_update", {})

    def set_hashing(self, device, name, size, readed, start_time):
        return self.sendmsg("set_hashing", {
            "device" : device,
            "name" : name,
            "size" : size,
            "readed" : readed,
            "start_time" : start_time
        })

    def inc_hashing(self, device, readed):
        return self.sendmsg("inc_hashing", {
            "device" : device,
            "readed" : readed
        })

    def set_imaging(self, name, count, processed, start_time):
        return self.sendmsg("set_imaging", {
            "name" : name,
            "count" : count,
            "processed" : processed,
            "start_time" : start_time
        })

    def inc_imaging(self, processed):
        return self.sendmsg("inc_imaging", {
            "processed" : processed
        })

    def quit(self):
        return self.sendmsg("quit", {}, 0.1)

    pass


#---------------------------------------------

class StatisticsManager(object):

    host = "127.0.1.3"
    port_min = 10000
    port_max = 20000
    port = None
    request = None

    address = None

    log = None

    def __init__(self):
        self.log = get_file_logger("StatisticsManager")
        pass

    def start_service(self):

        import random

        self.port = random.randint(self.port_min, self.port_max)

        self.address = (self.host, self.port,)

        from archive import which
        from subprocess import Popen

        Popen([which("python3"), "service/statistics.py",
               "--host", "%s" % self.host,
               "--port", "%s" % self.port,
        ])

        # WAIT WHILE SERVICE STARTS

        pings = 100
        runing = False
        t_begin = time()
        while not runing:

            pong = self.get_request().ping()

            if pong == "pong":
                runing = True

            pings -= 1
            if pings <= 0:
                break

        if pings <= 0:
            self.log.error("FAIL TO START service on %r in %s sec" % (
                self.address, timedelta(seconds= time() - t_begin),))
        else:
            self.log.info("STARTED service on %r in %s sec" % (
                self.address, timedelta(seconds=time() - t_begin),))

        return runing

    def sendmsg(self, task, data, timeout=default_request_timeout):
        try:
            result = sendmsg( self.address, {"task":task, "data":data}, timeout=timeout )
        except Exception as e:
            result = None
            self.log.error("SOCKET: %s" % ( e,))
            self.log.debug("FAIL SEND MESSAGE: task=%s, data=%r" % ( task, data,))
        return result

    def quit(self):
        return self.get_request().quit()

    def get_address(self):
        return self.address

    def get_request(self):
        if not self.request:
            self.request = StatisticsRequest(self)
        return self.request

    pass


#---------------------------------------------


def run_service(host, port):

    if hasattr(os, "nice"):
        os.nice(default_niceness_level)

    e_code = 0
    server = None
    try:
        SERVER_ADDRESS = (host, int(port),)

        server = StatisticsServer(SERVER_ADDRESS, StatisticsHandler, bind_and_activate=False)

        import signal

        signal.signal(signal.SIGINT, server.terminate_self)

        server.server_bind()
        server.server_activate()
        server.serve_forever()
    except Exception as e:
        e_code = 1
        if server and server.log:
            server.log.error("run_service: ERROR=%r" % (e,))
        pass

    return e_code

#---------------------------------------------


def launch():

    from argparse import ArgumentParser

    parser = ArgumentParser(description='Run statistics service.')
    parser.add_argument('--host', dest='host', action='store', type=str,
        help='Listen on TCP address')
    parser.add_argument('--port', dest='port', action='store', type=int,
        help='Listen on TCP port')

    args = parser.parse_args()
    return run_service(args.host, args.port)

if __name__ == "__main__":
    sys.exit(launch())
